package org.jeecg.config.mybatis;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.core.parser.SqlInfo;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;
import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.system.util.SystemUtils;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

@Intercepts({@Signature(
        type = StatementHandler.class,
        method = "prepare",
        args = {Connection.class, Integer.class}
)})
public class PaginationInterceptor extends com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor {
    private ISqlParser countSqlParser;
    private boolean overflow = false;
    private long limit = 500L;
    private String dialectType;
    private String dialectClazz;

    public PaginationInterceptor() {
    }

    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        this.sqlParser(metaObject);
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        if (SqlCommandType.SELECT == mappedStatement.getSqlCommandType() && StatementType.CALLABLE != mappedStatement.getStatementType()) {
            BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
            LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
            if (sysUser != null && !SystemUtils.checkMethod()) {

                String sOrgCode = SystemUtils.getCUserSOrgCode();
                String sql = boundSql.getSql();
                sql = sql.toUpperCase();
                if (!sql.contains("CREATE_BY") && !sql.contains("*")) {
                    sql = sql.replaceFirst("SELECT", "SELECT CREATE_BY,");
                }
                sql = "SELECT * FROM ( SELECT data.*,sys_user.org_code scode FROM ( " + sql;
                if (sql.contains("ORDER BY ")) {
                    sql = sql.replace("ORDER BY ", ") data LEFT JOIN sys_user ON data.create_by = sys_user.username ) a WHERE a.scode LIKE '" + sOrgCode + "%' ORDER BY a.");
                } else {
                    sql = sql + ") data LEFT JOIN sys_user ON data.create_by = sys_user.username ) a WHERE a.scode LIKE '" + sOrgCode + "%'";
                }

                Field declaredField = boundSql.getClass().getDeclaredField("sql");
                declaredField.setAccessible(true);
                declaredField.set(boundSql, sql);
            }


            Object paramObj = boundSql.getParameterObject();
            IPage<?> page = null;
            if (paramObj instanceof IPage) {
                page = (IPage) paramObj;
            } else if (paramObj instanceof Map) {
                Iterator var8 = ((Map) paramObj).values().iterator();

                while (var8.hasNext()) {
                    Object arg = var8.next();
                    if (arg instanceof IPage) {
                        page = (IPage) arg;
                        break;
                    }
                }
            }

            if (null != page && page.getSize() >= 0L) {
                if (this.limit > 0L && this.limit <= page.getSize()) {
                    page.setSize(this.limit);
                }

                String originalSql = boundSql.getSql();
                Connection connection = (Connection) invocation.getArgs()[0];
                DbType dbType = StringUtils.isNotEmpty(this.dialectType) ? DbType.getDbType(this.dialectType) : JdbcUtils.getDbType(connection.getMetaData().getURL());
                if (page.isSearchCount()) {
                    SqlInfo sqlInfo = SqlParserUtils.getOptimizeCountSql(page.optimizeCountSql(), this.countSqlParser, originalSql);
                    this.queryTotal(this.overflow, sqlInfo.getSql(), mappedStatement, boundSql, page, connection);
                    if (page.getTotal() <= 0L) {
                        return null;
                    }
                }

                String buildSql = concatOrderBy(originalSql, page);
                DialectModel model = DialectFactory.buildPaginationSql(page, buildSql, dbType, this.dialectClazz);
                Configuration configuration = mappedStatement.getConfiguration();
                List<ParameterMapping> mappings = new ArrayList(boundSql.getParameterMappings());
                Map<String, Object> additionalParameters = (Map) metaObject.getValue("delegate.boundSql.additionalParameters");
                model.consumers(mappings, configuration, additionalParameters);
                metaObject.setValue("delegate.boundSql.sql", model.getDialectSql());
                metaObject.setValue("delegate.boundSql.parameterMappings", mappings);
                return invocation.proceed();
            } else {
                return invocation.proceed();
            }
        } else {
            return invocation.proceed();
        }
    }


    public ISqlParser getCountSqlParser() {
        return countSqlParser;
    }

    public PaginationInterceptor setCountSqlParser(ISqlParser countSqlParser) {
        this.countSqlParser = countSqlParser;
        return this;
    }

    public boolean isOverflow() {
        return overflow;
    }

    @Override
    public PaginationInterceptor setOverflow(boolean overflow) {
        this.overflow = overflow;
        return this;
    }

    public long getLimit() {
        return limit;
    }

    @Override
    public PaginationInterceptor setLimit(long limit) {
        this.limit = limit;
        return this;
    }

    public String getDialectType() {
        return dialectType;
    }

    @Override
    public PaginationInterceptor setDialectType(String dialectType) {
        this.dialectType = dialectType;
        return this;
    }

    public String getDialectClazz() {
        return dialectClazz;
    }

    @Override
    public PaginationInterceptor setDialectClazz(String dialectClazz) {
        this.dialectClazz = dialectClazz;
        return this;
    }


}
